/* eslint-disable */  
export default class TreeMirror {
  constructor(root, delegate) {
    this.root = root;
    this.delegate = delegate;
    this.idMap = {};
  }

  initialize(rootId, children) {
    this.idMap[rootId] = this.root;
    for (let i = 0; i < children.length; i++) {
      this.deserializeNode(children[i], this.root);
    }
  }

  applyChanged(removed, addedOrMoved, attributes, text) {
    // NOTE: Applying the changes can result in an attempting to add a child
    // to a parent which is presently an ancestor of the parent. This can occur
    // based on random ordering of moves. The way we handle this is to first
    // remove all changed nodes from their parents, then apply.

    addedOrMoved.forEach((data) => {
      const node = this.deserializeNode(data);
      this.deserializeNode(data.parentNode);
      this.deserializeNode(data.previousSibling);
      if (node.parentNode) {
        node.parentNode.removeChild(node);
      }
    });

    removed.forEach((data) => {
      const node = this.deserializeNode(data);
      if (node.parentNode) {
        node.parentNode.removeChild(node);
      }
    });

    addedOrMoved.forEach((data) => {
      const node = this.deserializeNode(data);
      const parent = this.deserializeNode(data.parentNode);
      const previous = this.deserializeNode(data.previousSibling);
      parent.insertBefore(node, previous ? previous.nextSibling : parent.firstChild);
    });

    attributes.forEach((data) => {
      const node = this.deserializeNode(data);
      Object.keys(data.attributes).forEach((attrName) => {
        const newVal = data.attributes[attrName];
        if (newVal === null) {
          node.removeAttribute(attrName);
        }
        else {
          if (!this.delegate ||
            !this.delegate.setAttribute ||
            !this.delegate.setAttribute(node, attrName, newVal)) {
            try {
              node.setAttribute(attrName, newVal);
            } catch (e) {

            }
          }
        }
      });
    });

    text.forEach((data) => {
      const node = this.deserializeNode(data);
      node.textContent = data.textContent;
    });

    removed.forEach((node) => {
      delete this.idMap[node.id];
    });
  }

  deserializeNode(nodeData, parent) {
    if (nodeData === null) {
      return null;
    }
    let node = this.idMap[nodeData.id];

    if (node) {
      return node;
    }
    let doc = this.root.ownerDocument;
    if (doc === null) {
      doc = this.root;
    }
    try  {
      switch (nodeData.nodeType) {
        case Node.COMMENT_NODE:
          node = doc.createComment(nodeData.textContent);
          break;
        case Node.TEXT_NODE:
          node = doc.createTextNode(nodeData.textContent);
          break;
        case Node.DOCUMENT_TYPE_NODE:
          node = doc.implementation.createDocumentType(nodeData.name, nodeData.publicId, nodeData.systemId);
          break;
        case Node.ELEMENT_NODE:
          if (this.delegate && this.delegate.createElement)
            node = this.delegate.createElement(nodeData.tagName);
          if (!node)
            node = doc.createElement(nodeData.tagName);
          Object.keys(nodeData.attributes).forEach((name) => {
            if (!this.delegate ||
              !this.delegate.setAttribute ||
              !this.delegate.setAttribute(node, name, nodeData.attributes[name])) {
              if (typeof name === 'string' && name) {
                try {
                  node.setAttribute(name, nodeData.attributes[name]);
                } catch (e) {

                }
              }
            }
          });
          break;
        default:
          break;
      }
    } catch (e) {

    }

    try {
      if (!node) {
        throw {msg:'ouch',nodeData:JSON.stringify(nodeData)};
      }
      this.idMap[nodeData.id] = node;
      //console.log(nodeData.id)
      if (parent) {
        parent.appendChild(node);
      }
      if (nodeData.childNodes) {
        for (let i = 0; i < nodeData.childNodes.length; i++) {
          this.deserializeNode(nodeData.childNodes[i], node);
        }
      }
    } catch (e) {

    }

    return node;
  }
}
